package dan.frontend
import  chisel3._
import  chisel3.util._
import dan.utility.FrontendUtility.fetchIdx
import dan.utility.FrontendUtility.getPC
import dan.common.Consts.UOP_INVTLB

class LHPMeta extends Bundle with HasLHPParam{
    val phtIdx = UInt(lhtIdxBits.W)
    val state = UInt(2.W)
}

class LHPIO extends Bundle with HasLHPParam{
    val s0PC = Input(UInt(vaBits.W))
    // 这里只有强跳转是被采纳的
    val s2Taken = Output(Vec(fetchWidth, Valid(Bool())))
    val s3Meta = Output(Vec(fetchWidth, new LHPMeta()))
    val s1Update = Input(Valid(new PredictionUpdate()))
}

class LHP extends Module with HasLHPParam{
    val io = IO(new LHPIO())
    val lht = SyncReadMem(lhtSize, Vec(fetchWidth, UInt(historyLen.W)))
    val pht = Seq.fill(fetchWidth) {SyncReadMem(phtSize, UInt(2.W))}
    val resetEnable = RegInit(false.B)
    val resetIdx = RegInit(0.U(phtIdxBits.W))
    when(resetEnable){
        resetIdx := resetIdx + 1.U
    }
    // 注意只有pht大小为2的幂时有效
    when(resetIdx.andR){
        resetEnable := false.B
    }
    val s0Idx = fetchIdx(io.s0PC) 
    val s1PC = RegNext(io.s0PC)
    val s1History = lht.read(s0Idx(lhtIdxBits-1, 0))
    // 根据pc和历史获取PHT的索引
    // 遍历一个取值pack内的所有指令
    val s1PHTIdx = VecInit(s1History.zipWithIndex.map({case (history, i) => getHashedIdx(getPC(s1PC, i.U), history)}))
    // 读出PHT预测跳转与否
    // 遍历上述得到的所有索引
    val s2PHTEntry = VecInit(pht.zip(s1PHTIdx).map(({case (ram, idx) => ram.read(idx)})))
    io.s2Taken := VecInit(s2PHTEntry.map(
        state => {
            val taken = Wire(Valid(Bool()))
            taken.valid := state =/= 2.U && !resetEnable
            taken.bits := state(1)
            taken
        }
    ))
    val s2PHTIdx = RegNext(s1PHTIdx)
    val s2Meta = VecInit(s2PHTIdx.zip(s2PHTEntry).map({
        case(idx, counter) =>
            val newMeta = Wire(new LHPMeta())
            newMeta.phtIdx := idx
            newMeta.state := counter
            newMeta
    }
    ))
    io.s3Meta := RegNext(s2Meta)
    // 更新
    val s1Update = io.s1Update.bits
    val s1UpdatePC = s1Update.pc
    val s2Update = RegNext(s1Update)
    val s2OldHistory = lht.read(fetchIdx(s1UpdatePC)(lhtIdxBits-1, 0))
    // 每次更新都是以一个取值pack为单位
    val s2NewHistory = Wire(Vec(fetchWidth, UInt(historyLen.W)))
    val s2NewState = Wire(Vec(fetchWidth, UInt(2.W)))
    for(i <- 0 until fetchWidth){
        val s1Taken = Mux(s1Update.idx.valid && s1Update.idx.bits === i.U, s1Update.taken, false.B)
        val s2Taken = RegNext(s1Taken)
        val oldPHTIdx = s1Update.meta(i).lhpMeta.phtIdx
        val s2OldState = pht(i).read(oldPHTIdx)
        s2NewHistory(i) := Mux(s2Update.brMask(i), Cat(s2OldHistory(i)(historyLen-2, 0), s2Taken.asUInt), s2OldHistory(i))
        s2NewState(i) := Mux(s2Update.brMask(i), changeState(s2OldState, s2Taken), s2OldState)
    }
    when(RegNext(io.s1Update.valid)){
        lht.write(fetchIdx(s2Update.pc)(lhtIdxBits-1, 0), s2NewHistory)
    }
    when(RegNext(io.s1Update.valid) || resetEnable){
        for(i <- 0 until fetchWidth){
            pht(i).write(Mux(resetEnable, resetIdx, s2Update.meta(i).lhpMeta.phtIdx),
                Mux(resetEnable, 2.U(2.W), s2NewState(i))
            )
        }
    }
}